package com.pair.micro.fee.modules.charge.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pair.micro.common.constant.Constant;
import com.pair.micro.common.constant.MicroBusinessError;
import com.pair.micro.common.exception.MicroBusinessException;
import com.pair.micro.common.mybatis.service.impl.BaseServiceImpl;
import com.pair.micro.common.utils.DefinitionCode;
import com.pair.micro.common.utils.PageResult;
import com.pair.micro.fee.modules.charge.convert.ChargeReceiveConvert;
import com.pair.micro.fee.modules.charge.convert.ChargeReceiveConvert;
import com.pair.micro.fee.modules.charge.dao.ChargeProjectDao;
import com.pair.micro.fee.modules.charge.dao.ChargeReceiveDao;
import com.pair.micro.fee.modules.charge.dao.ChargeReceiveDao;
import com.pair.micro.fee.modules.charge.dao.RoomOwnerDao;
import com.pair.micro.fee.modules.charge.entity.ChargeProjectEntity;
import com.pair.micro.fee.modules.charge.entity.ChargeReceiveEntity;
import com.pair.micro.fee.modules.charge.entity.ChargeReceiveEntity;
import com.pair.micro.fee.modules.charge.entity.RoomOwnerEntity;
import com.pair.micro.fee.modules.charge.query.ChargeProjectQuery;
import com.pair.micro.fee.modules.charge.query.ChargeReceiveQuery;
import com.pair.micro.fee.modules.charge.service.ChargeProjectService;
import com.pair.micro.fee.modules.charge.service.ChargeReceiveService;
import com.pair.micro.fee.modules.charge.service.ChargeReceiveService;
import com.pair.micro.fee.modules.charge.vo.ChargeDetailVO;
import com.pair.micro.fee.modules.charge.vo.ChargeProjectVO;
import com.pair.micro.fee.modules.charge.vo.ChargeReceiveVO;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class ChargeReceiveServiceImpl extends BaseServiceImpl<ChargeReceiveDao, ChargeReceiveEntity> implements ChargeReceiveService {
    @Resource
    private DefinitionCode definitionCode;

    private final ChargeProjectDao chargeProjectDao;

//  @Lazy(true)
//  private final ChargeProjectService projectService;
    @Override
    public PageResult<ChargeReceiveVO> page(ChargeReceiveQuery query) {
        Map<String, Object> params = getParams(query);
        // 分页查询
        IPage<ChargeReceiveEntity> page = getPage(query);
        params.put(Constant.PAGE, page);
        List<ChargeReceiveEntity> list = baseMapper.getList(params);

        return new PageResult<>(ChargeReceiveConvert.INSTANCE.convertList(list), page.getTotal());
    }

    private Map<String, Object> getParams(ChargeReceiveQuery query) {
        Map<String, Object> params = new HashMap<>();
        params.put("revAmount", query.getRevAmount());
        return params;
    }

    @Override
    public void update(ChargeReceiveVO vo) {

    }

    @Override
    public void delete(List<Long> idList) {

    }

    @Override
    public List<ChargeProjectEntity> checkParams(ChargeReceiveVO vo) {
        Map<String, Object> params = new HashMap<>();
        if (Constant.GARDEN_RANGE.equals(vo.getChargeRange())){ //收费范围 小区 楼栋 单元 房屋
            params.put("gardenId",vo.getGardenId());
        } else if (Constant.HOUSE_RANGE.equals(vo.getChargeRange())){
            params.put("gardenId",vo.getGardenId());
            params.put("houseId",vo.getHouseId());
        } else if (Constant.UNIT_RANGE.equals(vo.getChargeRange())){
            params.put("gardenId",vo.getGardenId());
            params.put("houseId",vo.getHouseId());
            params.put("unitId",vo.getUnitId());
        } else if (Constant.ROOM_RANGE.equals(vo.getChargeRange())){
            params.put("gardenId",vo.getGardenId());
            params.put("houseId",vo.getHouseId());
            params.put("unitId",vo.getUnitId());
            params.put("roomId",vo.getRoomId());
        }
        params.put("configId",vo.getConfigVO().getId());
        List<ChargeProjectEntity> chargeProjectEntities = chargeProjectDao.getProjectList(params);
       if(chargeProjectEntities.size()==0) {
           throw  new MicroBusinessException(MicroBusinessError.BIS_50002);
       }
       return chargeProjectEntities;
    }

    //生成应收
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(List<ChargeProjectEntity> list, ChargeReceiveVO vo){
        List<ChargeReceiveEntity> receiveEntities = new ArrayList<>();
        // 建账时间           如果有两个收费项一个收费结束了  给另一个生成应收，判断条件：收费开始区间小于建账时间（因为新的收费项建账时间和开始时间相同）  并且 收费时间小于收费结束区间
       list.stream().filter(f -> f.getBeginTime().getTime() > vo.getChargeStartTime().getTime() && f.getStartTime().getTime() < vo.getChargeEndTime().getTime()).forEach(m -> {
            int x = 0;
            ChargeReceiveEntity entity = new ChargeReceiveEntity();
            ChargeProjectEntity project = new ChargeProjectEntity();
            BigDecimal unitPrice = vo.getConfigVO().getUnitPrice();
            BigDecimal area = new BigDecimal(String.valueOf(m.getArea()));
            BigDecimal additionalFee = vo.getConfigVO().getAdditionalFee();
            String chargeFormula = vo.getConfigVO().getChargeFormula();
            BigDecimal payFeeAmount;
            if (chargeFormula.equals(Constant.UNIT_PRICE_AREA)) { //如果公式为单价乘以面积加上附加费用 计算出月费用
                payFeeAmount = unitPrice.multiply(area).add(additionalFee).setScale(2, BigDecimal.ROUND_HALF_EVEN);
            } else if (chargeFormula.equals(Constant.FIXED_COSTS)) { //固定费用
                payFeeAmount = additionalFee.setScale(2, BigDecimal.ROUND_HALF_EVEN);
            } else {
                throw new MicroBusinessException(MicroBusinessError.BIS_20010, "暂时不支持该类公式");
            }
           Date monthBeginDate;
           if (m.getChargeState().equals(Constant.CHARGE_THIS_END)) { //当期收费结束后的继续生成应收
               //如果收费结束这里是月底最后一天的情况下加一天 TODO
               LocalDateTime monthBegin = LocalDateTime.ofInstant(m.getStartTime().toInstant(), ZoneId.systemDefault()).plusDays(1);
               monthBeginDate = toDate(monthBegin); //欠费开始时间
           } else {
               monthBeginDate = m.getStartTime();
           }

            LambdaQueryWrapper<ChargeReceiveEntity> wrapper = Wrappers.<ChargeReceiveEntity>lambdaQuery().eq(ChargeReceiveEntity::getProjectId, m.getId())
                    .eq(ChargeReceiveEntity::getState, 0).eq(ChargeReceiveEntity::getStatus, 0)
                    .orderByDesc(ChargeReceiveEntity::getPeriod).last("limit 1");
            ChargeReceiveEntity chargeReceiveEntity = baseMapper.selectOne(wrapper); //查询在用的 收费中状态的区间最大账单
            List<String> receiveTime = this.getReceiveTime(m.getStartTime(), vo.getChargeEndTime());
            if (chargeReceiveEntity != null) { //说明该收费项存在收费中状态的账单
                //过滤出来大于区间最大账单的集合
                receiveTime = receiveTime.stream().filter(item -> item.compareTo(chargeReceiveEntity.getPeriod()) > 0).collect(Collectors.toList());
            } else { //不存在收费中的账单,两种情况：1、原收费项没有账单 2、原收费项收费结束
                if (m.getChargeState().equals(Constant.CHARGING)) { //原收费项为收费中状态且没有账单
                    LocalDateTime startDay = LocalDateTime.ofInstant(m.getStartTime().toInstant(), ZoneId.systemDefault());
                    LocalDateTime last = LocalDateTime.from(startDay.with(TemporalAdjusters.lastDayOfMonth()));
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM"); // 定义格式化模式为"yyyy-MM"
                    String period = startDay.format(formatter); //费用区间

                    Date lastDate = toDate(last);
                    int lastDay = last.getDayOfMonth();
                    int currentDay = startDay.getDayOfMonth();//当月多少天
                    int spaceDay = lastDay - currentDay; //月最后一天减去起始时间天数
                    BigDecimal revAmountBim;
                    if (currentDay > 1) {
                        BigDecimal lastDayBim = new BigDecimal(lastDay);//当月多少天
                        BigDecimal days = new BigDecimal(spaceDay + 1); //算上起始日期当天 所以加上1
                        revAmountBim = payFeeAmount.divide(lastDayBim, 2, BigDecimal.ROUND_HALF_UP).multiply(days);//月费用除以当月天数乘以相差天数
                        entity.setRevAmount(revAmountBim);
                        entity.setReceiveAmount(revAmountBim);
                    } else {
                        entity.setRevAmount(payFeeAmount);
                        entity.setReceiveAmount(payFeeAmount);
                    }
                    String id = String.valueOf(definitionCode.nextId(DefinitionCode.CODE_PREFIX_CHARGE_RECEIVE));
                    entity.setDebtStartDate(monthBeginDate);
                    entity.setPaidAmount(new BigDecimal("0"));
                    entity.setProjectId(m.getId());
                    entity.setId(id);
                    entity.setPeriod(period);
                    entity.setStartDate(monthBeginDate);
                    entity.setEndDate(lastDate);
                    entity.setDebtEndDate(lastDate);//欠费结束时间
                    receiveEntities.add(entity);
                    x = 1;
                }
            }
            for (int i = x; i < receiveTime.size(); i++) {
                try {
                    ChargeReceiveEntity receiveEntity = new ChargeReceiveEntity();
                    String rid = String.valueOf(definitionCode.nextId(DefinitionCode.CODE_PREFIX_CHARGE_RECEIVE));
                    Date startDate = new SimpleDateFormat("yyyy-MM").parse(receiveTime.get(i));
                    LocalDateTime startEnd = LocalDateTime.ofInstant(startDate.toInstant(), ZoneId.systemDefault());
                    //获取当月第一天的日期
                    LocalDateTime startFirst = LocalDateTime.from(startEnd.with(TemporalAdjusters.firstDayOfMonth()));
                    Date startFirstDate = toDate(startFirst);
                    //获取当月最后一天的日期
                    LocalDateTime endLast = LocalDateTime.from(startEnd.with(TemporalAdjusters.lastDayOfMonth()));
                    Date endLastDate = toDate(endLast);

                    receiveEntity.setId(rid);
                    receiveEntity.setProjectId(m.getId());
                    receiveEntity.setPeriod(receiveTime.get(i));
                    receiveEntity.setStartDate(startFirstDate);//开始时间
                    receiveEntity.setEndDate(endLastDate);//结束时间
                    receiveEntity.setDebtStartDate(startFirstDate);//欠费开始时间
                    receiveEntity.setDebtEndDate(endLastDate);//欠费结束时间
                    receiveEntity.setReceiveAmount(payFeeAmount);
                    receiveEntity.setPaidAmount(new BigDecimal("0"));
                    receiveEntity.setRevAmount(payFeeAmount);
                    receiveEntities.add(receiveEntity);
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
            }
           //所有账单应收款相加
           BigDecimal sum = receiveEntities.stream().map(ChargeReceiveEntity::getReceiveAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
           project.setStartTime(monthBeginDate);
           project.setEndTime(vo.getChargeEndTime());
           project.setRevAmount(sum);
           project.setChargeState(Constant.CHARGING);
           project.setId(m.getId());
           //修改收费项
           chargeProjectDao.updateById(project);
        });
        saveBatch(receiveEntities);
    }

    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }
    public List<String> getReceiveTime(Date start, Date end) {
            List<String> list = new ArrayList<String>();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(start);
            int startYear = calendar.get(Calendar.YEAR);
            int startMonth = calendar.get(Calendar.MONTH);
            calendar.setTime(end);
            int endYear = calendar.get(Calendar.YEAR);
            int endMonth = calendar.get(Calendar.MONTH);
            for (int i = startYear; i <= endYear; i++) {
                String date = "";
                if (startYear == endYear) {
                    for (int j = startMonth; j <= endMonth; j++) {
                        if (j < 9) {
                            date = i + "-0" + (j + 1);
                        } else {
                            date = i + "-" + (j + 1);
                        }
                        list.add(date);
                    }
                } else {
                    if (i == startYear) {
                        for (int j = startMonth; j < 12; j++) {
                            if (j < 9) {
                                date = i + "-0" + (j + 1);
                            } else {
                                date = i + "-" + (j + 1);
                            }
                            list.add(date);
                        }
                    } else if (i == endYear) {
                        for (int j = 0; j <= endMonth; j++) {
                            if (j < 9) {
                                date = i + "-0" + (j + 1);
                            } else {
                                date = i + "-" + (j + 1);
                            }
                            list.add(date);
                        }
                    } else {
                        for (int j = 0; j < 12; j++) {
                            if (j < 9) {
                                date = i + "-0" + (j + 1);
                            } else {
                                date = i + "-" + (j + 1);
                            }
                            list.add(date);
                        }
                    }

                }
            }
        return list;
    }

    @Override
    public int getReceiveCount(ChargeDetailVO vo) {
        List<ChargeReceiveVO> receiveVO = vo.getReceiveVO();
        List<String> rids = receiveVO.stream().map(ChargeReceiveVO::getId).collect(Collectors.toList());
        LambdaQueryWrapper<ChargeReceiveEntity> receiveWrapper = new LambdaQueryWrapper<>();
        receiveWrapper.in(ChargeReceiveEntity::getId,rids).eq(ChargeReceiveEntity::getState,0);
        int count = baseMapper.selectCount(receiveWrapper).intValue();
        return count;
    }

}
